package gl.drawer

import org.lwjgl.opengl.ARBBindlessTexture.*
import org.lwjgl.opengl.GL45C.*
import org.lwjgl.stb.STBImage.stbi_image_free
import org.lwjgl.stb.STBImage.stbi_load

class Bindless: Drawer() {

    private var texture = 0
    private var textureHandle = 0L

    private var program = 0

    private var vao = 0
    private var vetBuffer = 0
    private var vertices = floatArrayOf(
        -0.8f, -0.8f, 0f, 1f,
         0.8f, -0.8f, 1f, 1f,
         0.8f,  0.8f, 1f, 0f,
        -0.8f,  0.8f, 0f, 0f,
    )

    override fun initialize() {
        // texture
        val width = IntArray(1)
        val height = IntArray(1)
        val image = stbi_load("D:\\lq\\gl\\app\\src\\main\\resources\\icon.png", width, height, IntArray(1), 4)
        check(image != null) { "Can't load image" }
        texture = glGenTextures()
        glBindTexture(GL_TEXTURE_2D, texture)
        glTextureStorage2D(GL_TEXTURE_2D, 1, GL_RGBA4, width[0], height[0])
        val imageBuffer = glGenBuffers()
        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, imageBuffer)
        glBufferStorage(GL_PIXEL_UNPACK_BUFFER, image, 0)
        stbi_image_free(image)
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width[0], height[0], GL_RGBA, GL_UNSIGNED_BYTE, 0)
        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0)
        glDeleteBuffers(imageBuffer)
        textureHandle = glGetTextureHandleARB(texture)
        glBindTexture(GL_TEXTURE_2D, 0)
        glMakeTextureHandleResidentARB(textureHandle)
        // program
        program = glCreateProgram()
        val vs = glCreateShader(GL_VERTEX_SHADER)
        glShaderSource(vs, vertexShader)
        glCompileShader(vs)
        check(glGetShaderi(vs, GL_COMPILE_STATUS) == GL_TRUE) { glGetShaderInfoLog(vs) }
        val fs = glCreateShader(GL_FRAGMENT_SHADER)
        glShaderSource(fs, fragmentShader)
        check(glGetShaderi(fs, GL_COMPILE_STATUS) == GL_TRUE) { glGetShaderInfoLog(fs) }
        glAttachShader(program, vs)
        glAttachShader(program, fs)
        glLinkProgram(program)
        glDetachShader(program, vs)
        glDetachShader(program, fs)
        glDeleteShader(vs)
        glDeleteShader(fs)
        check(glGetProgrami(fs, GL_LINK_STATUS) == GL_TRUE) { glGetProgramInfoLog(program) }
        glUseProgram(program)
        // vertex
        vetBuffer = glGenBuffers()
        glBindBuffer(GL_ARRAY_BUFFER, vetBuffer)
        glBufferStorage(GL_ARRAY_BUFFER, vertices, 0)
        vao = glGenVertexArrays()
        glBindVertexArray(vao)
        glEnableVertexAttribArray(0)
        glEnableVertexAttribArray(1)
        glDisableVertexAttribArray(2)
        glVertexAttribPointer(0, 2, GL_FLOAT, false, 16, 0)
        glVertexAttribPointer(1, 2, GL_FLOAT, false, 16, 8)
        glVertexAttribL1ui64ARB(2, textureHandle)
    }

    override fun update(): Boolean {
        super.update()
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4)
        return false
    }

    override fun terminal() {
        // vertex
        glBindVertexArray(0)
        glBindBuffer(GL_ARRAY_BUFFER, 0)
        glDeleteBuffers(0)
        glDeleteVertexArrays(vao)
        // program
        glUseProgram(0)
        glDeleteProgram(program)
        // image
        glMakeTextureHandleNonResidentARB(textureHandle)
        glBindTexture(GL_TEXTURE_2D, 0)
        glDeleteTextures(texture)
    }
}

private val vertexShader = """
#version 400 core
#extension GL_ARB_bindless_texture: require

layout(location=0) in vec2 vPosition;
layout(location=1) in vec2 vTexCoord;
layout(location=2) in sampler2D vTex;

out vec2 coord;
out sampler2D tex;

void main() {
    coord = vTexCoord;
    tex = vTex;
    gl_Position = vec4(vPosition, 0, 1);
}
""".trimIndent()

private val fragmentShader = """
#version 400 core
#extension GL_ARB_bindless_texture: require

in vec2 coord;
in sampler2D tex;
out vec4 fColor;

void main() {
    fColor = texture(tex, coord);
}
""".trimIndent()
